using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using JGSY.CMS.LowCode.Platform.Infrastructure.Audit;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Middleware
{
    /// <summary>
    /// 增强审计中间件
    /// 自动记录请求的详细信息和性能指标
    /// </summary>
    public class EnhancedAuditMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<EnhancedAuditMiddleware> _logger;
        private readonly EnhancedAuditOptions _options;

        public EnhancedAuditMiddleware(
            RequestDelegate next,
            ILogger<EnhancedAuditMiddleware> logger,
            EnhancedAuditOptions options)
        {
            _next = next;
            _logger = logger;
            _options = options;
        }

        public async Task InvokeAsync(HttpContext context, IAuditLogAppService auditService)
        {
            // 跳过不需要审计的路径
            if (ShouldSkipAudit(context))
            {
                await _next(context);
                return;
            }

            var auditContext = new EnhancedAuditContext(
                new HttpContextAccessor { HttpContext = context },
                context.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger<EnhancedAuditContext>());

            var originalBodyStream = context.Response.Body;
            string? responseBody = null;

            try
            {
                // 记录请求体（如果配置允许）
                string? requestBody = null;
                if (_options.LogRequestBody && IsLoggableContentType(context.Request.ContentType))
                {
                    requestBody = await ReadRequestBodyAsync(context);
                }

                // 记录响应体（如果配置允许）
                if (_options.LogResponseBody)
                {
                    using var responseBodyStream = new MemoryStream();
                    context.Response.Body = responseBodyStream;

                    await _next(context);

                    auditContext.SetStatusCode(context.Response.StatusCode);

                    // 读取响应体
                    if (IsLoggableContentType(context.Response.ContentType))
                    {
                        responseBodyStream.Seek(0, SeekOrigin.Begin);
                        responseBody = await new StreamReader(responseBodyStream).ReadToEndAsync();
                    }

                    // 将响应体写回原始流
                    responseBodyStream.Seek(0, SeekOrigin.Begin);
                    await responseBodyStream.CopyToAsync(originalBodyStream);
                }
                else
                {
                    await _next(context);
                    auditContext.SetStatusCode(context.Response.StatusCode);
                }

                auditContext.Stop();

                // 记录审计日志
                await LogAuditAsync(auditContext, auditService, requestBody, responseBody, null);
            }
            catch (Exception ex)
            {
                auditContext.Stop();
                auditContext.SetStatusCode(500);

                // 记录异常审计日志
                await LogAuditAsync(auditContext, auditService, null, null, ex);
                throw;
            }
            finally
            {
                context.Response.Body = originalBodyStream;
                auditContext.Dispose();
            }
        }

        /// <summary>
        /// 判断是否应跳过审计
        /// </summary>
        private bool ShouldSkipAudit(HttpContext context)
        {
            var path = context.Request.Path.Value?.ToLowerInvariant();
            if (string.IsNullOrEmpty(path)) return false;

            return _options.SkipPaths.Any(skipPath => path.StartsWith(skipPath.ToLowerInvariant()));
        }

        /// <summary>
        /// 判断内容类型是否可记录
        /// </summary>
        private static bool IsLoggableContentType(string? contentType)
        {
            if (string.IsNullOrEmpty(contentType)) return false;

            var loggableTypes = new[]
            {
                "application/json",
                "application/xml",
                "text/",
                "application/x-www-form-urlencoded"
            };

            return loggableTypes.Any(type => contentType.StartsWith(type, StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// 读取请求体
        /// </summary>
        private static async Task<string?> ReadRequestBodyAsync(HttpContext context)
        {
            try
            {
                context.Request.EnableBuffering();

                using var reader = new StreamReader(
                    context.Request.Body,
                    encoding: Encoding.UTF8,
                    detectEncodingFromByteOrderMarks: false,
                    bufferSize: 1024,
                    leaveOpen: true);

                var body = await reader.ReadToEndAsync();
                context.Request.Body.Position = 0;

                return string.IsNullOrEmpty(body) ? null : body;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// 记录审计日志
        /// </summary>
        private async Task LogAuditAsync(
            EnhancedAuditContext auditContext,
            IAuditLogAppService auditService,
            string? requestBody,
            string? responseBody,
            Exception? exception)
        {
            try
            {
                var auditType = exception != null ? "Error" : 
                               auditContext.StatusCode >= 400 ? "Warning" : "Information";

                var content = BuildAuditContent(auditContext, requestBody, responseBody, exception);
                var result = exception != null ? "Error" : 
                            auditContext.StatusCode >= 400 ? "Failed" : "Success";

                var auditLog = auditContext.CreateAuditLog(
                    type: auditType,
                    content: content,
                    result: result,
                    resource: $"{auditContext.HttpMethod} {auditContext.RequestPath}",
                    exception: exception?.ToString(),
                    extendedData: BuildExtendedData(auditContext, requestBody, responseBody)
                );

                await auditService.AddAsync(MapToDto(auditLog));

                // 记录结构化日志
                using var scope = _logger.BeginScope(new Dictionary<string, object>
                {
                    ["CorrelationId"] = auditContext.CorrelationId ?? "",
                    ["TenantId"] = auditContext.TenantId ?? "",
                    ["Duration"] = auditContext.Duration ?? 0,
                    ["StatusCode"] = auditContext.StatusCode ?? 0
                });

                if (exception != null)
                {
                    _logger.LogError(exception, "Request failed: {Method} {Path}", 
                        auditContext.HttpMethod, auditContext.RequestPath);
                }
                else if (auditContext.Duration > _options.SlowRequestThresholdMs)
                {
                    _logger.LogWarning("Slow request detected: {Method} {Path} took {Duration}ms",
                        auditContext.HttpMethod, auditContext.RequestPath, auditContext.Duration);
                }
                else
                {
                    _logger.LogInformation("Request completed: {Method} {Path} returned {StatusCode}",
                        auditContext.HttpMethod, auditContext.RequestPath, auditContext.StatusCode);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to log audit information");
            }
        }

        /// <summary>
        /// 构建审计内容
        /// </summary>
        private static string BuildAuditContent(
            EnhancedAuditContext auditContext,
            string? requestBody,
            string? responseBody,
            Exception? exception)
        {
            var content = new StringBuilder();
            content.AppendLine($"Request: {auditContext.HttpMethod} {auditContext.RequestPath}");
            content.AppendLine($"User: {auditContext.UserName} ({auditContext.UserId})");
            content.AppendLine($"IP: {auditContext.IpAddress}");
            content.AppendLine($"Duration: {auditContext.Duration}ms");
            content.AppendLine($"Status: {auditContext.StatusCode}");

            if (!string.IsNullOrEmpty(auditContext.TenantId))
                content.AppendLine($"Tenant: {auditContext.TenantId}");

            if (!string.IsNullOrEmpty(auditContext.PermissionCode))
                content.AppendLine($"Permission: {auditContext.PermissionCode}");

            if (exception != null)
                content.AppendLine($"Error: {exception.Message}");

            return content.ToString();
        }

        /// <summary>
        /// 构建扩展数据
        /// </summary>
        private static string? BuildExtendedData(
            EnhancedAuditContext auditContext,
            string? requestBody,
            string? responseBody)
        {
            var extendedData = new
            {
                RequestBody = requestBody,
                ResponseBody = responseBody,
                UserAgent = auditContext.UserAgent,
                Headers = new { } // 可以根据需要添加请求头信息
            };

            return System.Text.Json.JsonSerializer.Serialize(extendedData);
        }

        /// <summary>
        /// 映射到DTO
        /// </summary>
        private static Application.DTOs.AuditLogDto MapToDto(Domain.Entities.AuditLog auditLog)
        {
            return new Application.DTOs.AuditLogDto
            {
                Type = auditLog.Type,
                Content = auditLog.Content,
                UserId = auditLog.UserId,
                UserName = auditLog.UserName,
                Timestamp = auditLog.Timestamp,
                IP = auditLog.IP,
                Result = auditLog.Result,
                Exception = auditLog.Exception,
                Resource = auditLog.Resource,
                TenantId = auditLog.TenantId,
                UserAgent = auditLog.UserAgent,
                Duration = auditLog.Duration,
                CorrelationId = auditLog.CorrelationId,
                HttpMethod = auditLog.HttpMethod,
                RequestPath = auditLog.RequestPath,
                StatusCode = auditLog.StatusCode,
                PermissionCode = auditLog.PermissionCode,
                ExtendedData = auditLog.ExtendedData
            };
        }
    }

    /// <summary>
    /// 增强审计配置选项
    /// </summary>
    public class EnhancedAuditOptions
    {
        /// <summary>
        /// 是否记录请求体
        /// </summary>
        public bool LogRequestBody { get; set; } = false;

        /// <summary>
        /// 是否记录响应体
        /// </summary>
        public bool LogResponseBody { get; set; } = false;

        /// <summary>
        /// 跳过审计的路径列表
        /// </summary>
        public List<string> SkipPaths { get; set; } = new()
        {
            "/health",
            "/metrics",
            "/swagger",
            "/favicon.ico",
            "/_vs/",
            "/css/",
            "/js/",
            "/images/"
        };

        /// <summary>
        /// 慢请求阈值（毫秒）
        /// </summary>
        public long SlowRequestThresholdMs { get; set; } = 5000;
    }
}
